<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>web chat</title>
</head>
<body>
<input type="button" value="连接" onclick="wsSession.open()"/>
<input type="button" value="重连" onclick="wsSession.reopen()"/>
<input type="button" value="断开" onclick="wsSession.close()"/>
<input type="button" value="在线" onclick="console.info(wsSession.online());"/>

<script>

    // 1.可创建连接对象
    // 2.包含心跳检测，可设置心跳规则
    // 3.可主动切断，连接和重连
    // 4.可设置连接，消息，断开，错误，提示等事件回调
    // {
    //     url = "",
    //         // 设置默认的心跳控制
    //         delay = 1000,        // 默认延迟60s
    //         ping = "ping",       // 该实例发送ping 表示服务端收到
    //         pong = "pong",       // 该实例收到pong 表示服务端回复
    //         count = 2,           // 服务端延迟回复最大次数，该次数 * 延时，为判断服务回复心跳的最长时间否则客户端主动切断连接
    //         connected = function(){},    // 默认值回调函数
    //         message = function(){},
    //         disconnect = function(){},
    //         error = function(){},
    //         operate = function(){},
    // } = {}

    function WsSession({url, delay = 1000, ping = "ping", count = 2, connected, message, disconnect, error} = {}) {
        let _regExp = /ws[s]?:\/\/([\w.]+\/?)\S*/;
        let _tips = {
            connected: "已经建立连接！",
            disconnected: "已经断开连接！",
            notSupport: "该浏览器不支持WebSocket，请更换最新的浏览器！！！",
            errUrl: "地址为空或者不符合ws连接表达式",
        };

        this.connected = connected || function (ws, ev) {
            console.info("建立连接");
        };
        this.message = message || function (data, ws, ev) {
            console.info("消息: ", data);
        };
        this.disconnect = disconnect || function (ev) {
            console.info("断开连接", ev);
        };
        this.error = error || function (err) {
        };
        this.operate = function (obj) {
            console.info(obj);
        };

        let _this = this;

        if (!"WebSocket" in window) {
            _this.error(_tips.notSupport);
            return;
        } else if (!_regExp.test(url)) {
            _this.error(_tips.errUrl);
            return;
        }

        let _ws = null;
        let _keepAlive = null;
        let _clientTimer = null;
        let _serverTimer = null;

        _create();

        function _ping() {
            _clientTimer = setTimeout(() => {
                if (_keepAlive && _ws.readyState === 1)
                    _ws.send(ping);
                _serverTimer = setTimeout(() => {
                    if (_ws.readyState === 1)
                        _ws.close();
                }, delay * count)
            }, delay)
        }

        function _reset() {
            clearTimeout(_clientTimer);
            clearTimeout(_serverTimer);
            _ping();
        }

        function _create() {
            _ws = new WebSocket(url);
            _ws.onopen = function (ev) {
                _this.connected(_this, ev);
                _ping();
                _this.operate(_tips.connected);
            };
            _ws.onmessage = function (ev) {
                _reset();
                let data = ev.data;
                if (data !== ping)
                    _this.message(data, _this, ev);
            };
            _ws.onclose = function (ev) {
                _this.operate(_tips.disconnected);
                _this.disconnect(ev);
                if (_keepAlive) _create();
            };
            _ws.onerror = function (ev) {
                console.error(ev);
                _this.error(ev);
            };
            _keepAlive = true;
        }

        this.open = function () {
            if (_keepAlive) {
                this.operate(_tips.connected);
                return;
            }
            if (_clientTimer) clearTimeout(_clientTimer);
            if (_serverTimer) clearTimeout(_serverTimer);
            _create();
        };
        this.reopen = function () {
            this.close();
            setTimeout(() => {
                this.open();
            }, 1000);
        };
        this.close = function () {
            if (!_keepAlive) {
                this.operate(_tips.disconnected);
                return;
            }
            if (_ws && _ws.readyState === 1) {
                _keepAlive = false;
                _ws.close();
            }
        };
        this.online = function () {
            return _keepAlive && _ws.readyState === 1;
        };
        this.send = function (data) {
            if (_ws && _ws.readyState === 1) {
                _ws.send(data);
            }
        };
    }

    let wsUrl = "ws://" + window.location.host + "/conn.ws";

    for (let i = 0; i < 201; i++) {
        let wsArr = [];
        wsArr.push(new WsSession({
            url: wsUrl,
            delay: 60 * 1000,
            ping: "ping",
            count: 2
        }))
    }
    // setTimeout(() => {
    //     ws.close();
    // }, 4700);
    // setTimeout(() => {
    //     ws.reopen();
    //     setInterval(() => {
    //         ws.send("ping")
    //     }, 800);
    // }, 6000);

</script>
</body>
</html>